/* Copyright 2012 Dustin Mitchell <dmmitche [at] gmail [dot] com>
 *
 * Licensed under the GPLv3.  See included LICENSE.txt for full details.
 */

#include <stdint.h>
#include <stdlib.h>

#include "clock.h"
#include "registers.h"
#include "log.h"

#define CM_PER_REG_START 0x44e00000
#define CM_PER_REG_SIZE SIZE_1K
#define CLOCK_ENABLE 0x2
#define CLOCK_DISABLE 0x0

static int CLOCK_INITIALIZED = 0;
static struct reg_map CM_PER_REG_MAP;
static volatile uint32_t *CM_PER_REG_ADDR;

static int clock_write (enum clock_module mod, int value)
{
  if (!CLOCK_INITIALIZED) {
    log (LOG_ERR, "clock module uninitialized");
    return -1;
  }

  if (mod == CLOCK_NONE)
    return 0;

  CM_PER_REG_ADDR[mod / sizeof (uint32_t)] = value;

  return 0;
}

static void clock_cleanup ()
{
  if (!CLOCK_INITIALIZED)
    return;

  registers_unmap (&CM_PER_REG_MAP);
}



int clock_init ()
{
  if (CLOCK_INITIALIZED)
    return -1;

  if (atexit (clock_cleanup) != 0) {
    log (LOG_ERR, "unable to install clock_cleanup");
    return -1;
  }

  if (registers_map (&CM_PER_REG_MAP, CM_PER_REG_START, CM_PER_REG_SIZE)
      == -1) {
    log (LOG_ERR, "unable to map clock registers");
    return -1;
  }

  CM_PER_REG_ADDR = registers_get_reg_addr (&CM_PER_REG_MAP, 0, uint32_t);

  CLOCK_INITIALIZED = 1;

  return 0;
}

int clock_is_inited ()
{
  return CLOCK_INITIALIZED;
}

int clock_enable (enum clock_module mod)
{
  return clock_write (mod, CLOCK_ENABLE);
}

int clock_disable (enum clock_module mod)
{
  return clock_write (mod, CLOCK_DISABLE);
}
